#include "HEMUIRParse.h"
#include <cctype>

extern HEMUIRFTb func_tb;
extern HEMUIRFInsTb func_inst_tb;
extern HEMUIRGloTb glo_tb;


/* set immediate operand value
 * deal with one word
 * I32: u0x
 * FLOAT || DOUBLE: 0x
 * POINTER: null
 * return true or false
 */
bool set_imm_val(const std::vector<std::string>& toks, size_t pos, HEMUIRVar& imm) {
	if (imm.btype == HEMUIRBtype::I8) {
		if (toks.at(pos).at(0) == '0')
			imm.val.i8_val = 0;
		else if (toks.at(pos).at(0) == '1')
			imm.val.i8_val = 1;
		else
			assert(0);
		return true;
	}
	else if (imm.btype == HEMUIRBtype::I32) {
		if (toks.at(pos).substr(0, 3) == "u0x") {
			imm.val.i64_val = std::stoll(toks.at(pos).substr(3), nullptr, 16);
			return true;
		}
		else if (isdigit(toks.at(pos).at(0)) || toks.at(pos).at(0) == '-') {
			imm.val.i64_val = std::stoll(toks.at(pos).substr(0));
			return true;
		}
		else
			assert(0);
	}
	else if (imm.btype == HEMUIRBtype::FLOAT || imm.btype == HEMUIRBtype::DOUBLE) {
		if (toks.at(pos).substr(0, 2) == "0x") {
			imm.val.i64_val = std::stoull(toks.at(pos).substr(2), nullptr, 16);
			return true;
		}
		else
			assert(0);
	}
	else if (imm.btype == HEMUIRBtype::POINTER) {
		if (toks.at(pos) == "null") {
			imm.val.ptr_val = nullptr;
			return true;
		}
		else
			assert(0);
	}
	return false;
}

/*
 * get base type
 * return HEMUIRBtype
 */
HEMUIRBtype get_btype(std::string tp) {
	if (tp == "i8") {
		return HEMUIRBtype::I8;
	}
	else if (tp == "i32") {
		return HEMUIRBtype::I32;
	}
	else if (tp == "float") {
		return HEMUIRBtype::FLOAT;
	}
	else if (tp == "double") {
		return HEMUIRBtype::DOUBLE;
	}
	else if (tp == "ptr") {
		return HEMUIRBtype::POINTER;
	}
	else if (tp == "void") {
		return HEMUIRBtype::VOID;
	}
	else {
		return HEMUIRBtype::UNKNOWN;
	}

}

/* get cond
 * return CMPCond
 */
CMPCond get_cond(const std::string& tok) {
	if (tok == "eq" || tok == "oeq")
		return CMPCond::EQ;
	else if (tok == "ne" || tok == "one")
		return CMPCond::NE;
	else if (tok == "sgt" || tok == "ogt")
		return CMPCond::GT;
	else if (tok == "sge" || tok == "oge")
		return CMPCond::GE;
	else if (tok == "slt" || tok == "olt")
		return CMPCond::LT;
	else
		return CMPCond::LE;
}


/*
 * set type
 * include vector and scalar
 * return num of words
 */
size_t set_type(const std::vector<std::string>& toks, size_t pos, HEMUIRType& type) {
	if (isdigit(toks.at(pos).at(0))) {
		type.length = std::stoll(toks.at(pos));
		type.base_type = get_btype(toks.at(pos + 2));
		return 3;
	}
	else {
		type.length = 1;
		type.base_type = get_btype(toks.at(pos));
		return 1;
	}
}

/* set immediate operand value and type
 * deal with two words
 * I32: u0x
 * FLOAT || DOUBLE: 0x
 * POINTER: null
 * return true or false
 */
bool set_imm(const std::vector<std::string>& toks, size_t pos, HEMUIRVar& imm) {
	imm.btype = get_btype(toks.at(pos));
	if (imm.btype == HEMUIRBtype::UNKNOWN)
		return false;
	return  set_imm_val(toks, pos + 1, imm);
}

/* set operand value and type
 * deal with two words
 * return true or false
 */
bool set_opnd(const std::vector<std::string>& toks, size_t pos, HEMUIROpnd& opnd) {
	if (toks.at(pos) == "void") {
		opnd.tp = HEMUIROpnd::IMM;
		opnd.imm.btype = HEMUIRBtype::VOID;
		return true;
	}
	else if (toks.at(pos + 1).at(0) == '@') {
		opnd.tp = HEMUIROpnd::GLOVAR;
		std::string name = toks.at(pos + 1).substr(1);
		opnd.glovar_idx = glo_tb.search_glo(name);
		if (opnd.glovar_idx != SIZE_MAX)
			return true;
		else
			return false;
	}
	else if (toks.at(pos + 1).at(0) == '%') {
		opnd.tp = HEMUIROpnd::TEMP;
		opnd.temp_idx = std::stoll(toks.at(pos + 1).substr(1));
		return true;
	}
	else {
		opnd.tp = HEMUIROpnd::IMM;
		return set_imm(toks, pos, opnd.imm);
	}
}

/* set operand value
 * deal with owe word
 * return true or false
 */
bool set_opnd(const std::vector<std::string>& toks, size_t pos, HEMUIRBtype tp, HEMUIROpnd& opnd) {
	if (toks.at(pos).at(0) == '@') {
		opnd.tp = HEMUIROpnd::GLOVAR;
		std::string name = toks.at(pos).substr(1);
		opnd.glovar_idx = glo_tb.search_glo(name);
		if (opnd.glovar_idx != SIZE_MAX)
			return true;
		else
			return false;
	}
	else if (toks.at(pos).at(0) == '%') {
		opnd.tp = HEMUIROpnd::TEMP;
		opnd.temp_idx = std::stoll(toks.at(pos).substr(1));
		return true;
	}
	else {
		opnd.tp = HEMUIROpnd::IMM;
		opnd.imm.btype = tp;
		return set_imm_val(toks, pos, opnd.imm);
	}


}

/*
 * set function args
 * type to check
 */
bool set_fargs(const std::vector<std::string>& toks, size_t pos, std::vector<HEMUIROpnd>& fargs) {
	size_t toks_size = toks.size();
	for (; pos < toks_size; pos += 2) {
		HEMUIROpnd farg;
		set_opnd(toks, pos, farg);
		fargs.push_back(farg);
	}
	return true;
}


/*
 * remove comments in the line
 */
bool rm_comm(std::string& line) {
	if (line.size() != 0) {
		size_t cpos = line.find(';');
		if (cpos != std::string::npos) {
			line = line.substr(0, cpos);
			return true;
		}
	}
	return false;
}

/*
 * parse by delim
 */
bool parse(const std::string& line, const std::string& delim, std::vector<std::string>& toks) {
	size_t slen = line.size();
	if (slen != 0) {
		size_t posl = 0;
		while (posl < slen) {
			size_t posr = line.find_first_of(delim, posl);
			if (posr > posl) {
				std::string tok = line.substr(posl, posr - posl);
				toks.push_back(tok);
			}
			if (posr >= slen)
				break;
			posl = posr + 1;
		}
		return true;
	}
	return false;
}

/*						set inst by op types							*/
/*						lineno need to be set bedore					*/

bool op_nop_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	inst.oper = HEMUIROper::NOP;
	return true;
}

bool op_ret_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	inst.oper = HEMUIROper::RET;
	set_type(toks, 1, inst.type);
	return set_opnd(toks, 1, inst.value);
}

bool op_br_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	if (toks.size() > 4) {
		inst.oper = HEMUIROper::BRc;
		bool b1 = set_opnd(toks, 1, inst.cond);
		bool b2 = set_opnd(toks, 3, inst.ifture);
		bool b3 = set_opnd(toks, 5, inst.iffalse);
		return b1 && b2 && b3;
	}
	else {
		inst.oper = HEMUIROper::BRu;
		return set_opnd(toks, 1, inst.brdest);
	}
}

bool op_fneg_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	inst.oper = HEMUIROper::FNEG;
	set_type(toks, 3, inst.type);

	bool b1 = set_opnd(toks, 0, inst.type.base_type, inst.dest);
	bool b2 = set_opnd(toks, 3, inst.type.base_type, inst.op1);
	return b1 && b2;
}

bool op_calc_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	if (toks.at(2) == "add")
		inst.oper = HEMUIROper::ADD;
	else if (toks.at(2) == "fadd")
		inst.oper = HEMUIROper::FADD;
	else if (toks.at(2) == "sub")
		inst.oper = HEMUIROper::SUB;
	else if (toks.at(2) == "fsub")
		inst.oper = HEMUIROper::FSUB;
	else if (toks.at(2) == "mul")
		inst.oper = HEMUIROper::MUL;
	else if (toks.at(2) == "fmul")
		inst.oper = HEMUIROper::FMUL;
	else if (toks.at(2) == "udiv")
		inst.oper = HEMUIROper::UDIV;
	else if (toks.at(2) == "sdiv")
		inst.oper = HEMUIROper::SDIV;
	else if (toks.at(2) == "fdiv")
		inst.oper = HEMUIROper::FDIV;
	else if (toks.at(2) == "urem")
		inst.oper = HEMUIROper::UREM;
	else if (toks.at(2) == "srem")
		inst.oper = HEMUIROper::SREM;
	else if (toks.at(2) == "shl")
		inst.oper = HEMUIROper::SHL;
	else if (toks.at(2) == "lshr")
		inst.oper = HEMUIROper::LSHR;
	else if (toks.at(2) == "ashr")
		inst.oper = HEMUIROper::ASHR;
	else if (toks.at(2) == "and")
		inst.oper = HEMUIROper::AND;
	else if (toks.at(2) == "or")
		inst.oper = HEMUIROper::OR;
	else if (toks.at(2) == "xor")
		inst.oper = HEMUIROper::XOR;

	inst.type.base_type = get_btype(toks.at(3));
	inst.type.length = 1;

	bool b1 = set_opnd(toks, 0, inst.type.base_type, inst.dest);
	bool b2 = set_opnd(toks, 4, inst.type.base_type, inst.op1);
	bool b3;
	if (inst.oper == HEMUIROper::ADD && inst.type.base_type == HEMUIRBtype::POINTER)
		b3 = set_opnd(toks, 5, HEMUIRBtype::I32, inst.op2);
	else
		b3 = set_opnd(toks, 5, inst.type.base_type, inst.op2);

	return b1 && b2 && b3;
}

bool op_alloc_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	inst.oper = HEMUIROper::ALLOCA;
	size_t len = set_type(toks, 3, inst.type);
	bool b1 = set_opnd(toks, 0, HEMUIRBtype::POINTER, inst.dest);
	inst.alignment = std::stoll(toks.at(4 + len));
	return b1;
}

bool op_load_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	inst.oper = HEMUIROper::LOAD;
	set_type(toks, 3, inst.type);
	bool b1 = set_opnd(toks, 0, inst.type.base_type, inst.dest);
	bool b2 = set_opnd(toks, 4, inst.pointer);
	return b1 && b2;
}

bool op_store_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	inst.oper = HEMUIROper::STORE;
	set_type(toks, 1, inst.type);
	bool b1 = set_opnd(toks, 1, inst.value);
	bool b2 = set_opnd(toks, 3, inst.pointer);
	return b1 && b2;
}

bool op_cmp_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	if (toks.at(2).at(0) == 'f')
		inst.oper = HEMUIROper::FCMP;
	else
		inst.oper = HEMUIROper::ICMP;

	inst.cmpcond = get_cond(toks.at(3));
	set_type(toks, 4, inst.type);

	bool b1 = set_opnd(toks, 0, HEMUIRBtype::I8, inst.dest);
	bool b2 = set_opnd(toks, 5, inst.type.base_type, inst.op1);
	bool b3 = set_opnd(toks, 6, inst.type.base_type, inst.op2);

	return b1 && b2 && b3;
}

bool op_call_ir(const std::vector<std::string>& toks, HEMUIRInst& inst) {
	inst.oper = HEMUIROper::CALL;
	inst.call_dest = false;
	size_t dest_off = 0;
	if (toks.at(1).at(0) == '=') {
		dest_off = 2;
		inst.call_dest = true;
	}

	set_type(toks, 1 + dest_off, inst.type);
	if (dest_off == 2) {
		set_opnd(toks, 0, inst.type.base_type, inst.dest);
	}
	inst.func_name = toks.at(2 + dest_off).substr(1);
	inst.funcidx = func_tb.search_idx(toks.at(2 + dest_off).substr(1));
	set_fargs(toks, 3 + dest_off, inst.fargs);
	return true;
}








































